package demo.json.gson;

import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import com.google.gson.JsonObject;
import com.google.gson.TypeAdapter;
import com.google.gson.reflect.TypeToken;
import com.google.gson.stream.JsonReader;
import com.google.gson.stream.JsonWriter;
import demo.vo.TimeDemoVO;
import demo.vo.User;
import org.junit.Test;

import java.io.IOException;
import java.time.*;
import java.util.Date;
import java.util.List;
import java.util.Map;

/**
 * GSON 是 Google 开源的一款 JSON 序列化 / 反序列化库，用于将 Java 对象与 JSON 字符串之间快速、灵活地转换，无需手动解析 JSON 或拼接字符串。
 * 它支持复杂对象结构、泛型、自定义序列化规则，且轻量无依赖，是 Java 生态中最常用的 JSON 处理工具之一。
 * <p>
 * 常见问题与注意事项
 *
 * <li>反序列化必需无参构造器：JavaBean 必须提供无参构造器，否则会抛出 IllegalStateException；
 * <li>字段访问权限：GSON 可访问 private 字段，但建议通过 getter/setter 封装，或确保字段可见；
 * <li>泛型擦除问题：反序列化泛型集合（如 List<User>）时，必须使用 TypeToken 保留泛型信息，不能直接用 List.class；
 * <li>null 值处理：默认情况下，GSON 不序列化 null 字段，可通过 serializeNulls() 开启；
 * <li>日期时间转换：默认 Date 类型会序列化为时间戳（毫秒数），建议通过 setDateFormat() 或自定义 TypeAdapter 指定格式。
 */
public class GsonDemo {
    /**
     * 简单对象序列化（JavaBean → JSON）
     */
    @Test
    public void testObject2Json() {
        // 1. 创建 Gson 实例（全局单例即可）
        Gson gson = new Gson();

        // 2. 创建 Java 对象
        User user = new User();
        user.setName("张三");
        user.setAge(25);
        user.setLovers(List.of("李四", "王五"));
        user.setInfo(Map.of("gender", "male", "height", 1.75));

        // 3. 序列化：Java 对象 → JSON 字符串
        String json = gson.toJson(user);

        System.out.println(json);
        // 输出：{"name":"张三","age":25,"email":"zhangsan@example.com"}

        User user2 = new User();
        user2.setName("马晓怡");
        user2.setAge(25);
        user2.setLovers(List.of("Stan", "Han"));
        user2.setInfo(Map.of("gender", "male", "height", 1.75));
        List<User> list = List.of(user, user2);
        String jsonList = gson.toJson(list);
        System.out.println(jsonList);


    }

    /**
     * 简单对象反序列化（JSON → JavaBean）
     */
    @Test
    public void testJson2Object() {
        // 1. 创建 Gson 实例（全局单例即可）
        Gson gson = new Gson();

        // 2. JSON 字符串
        String json = """
                {"name":"张三","age":25,"lovers":["李四","王五"],"info":{"gender":"male","height":1.75}}
                """;

        // 3. 反序列化：JSON 字符串 → Java 对象
        User user = gson.fromJson(json, User.class);

        System.out.println(user.getInfo());

        String jsonList = """
                [{"name":"张三","age":25,"lovers":["李四","王五"],"info":{"gender":"male","height":1.75}},
                {"name":"马晓怡","age":25,"lovers":["Stan","Han"],"info":{"gender":"male","height":1.75}}]
                """;
        // 关键：通过 TypeToken 声明 List<User> 类型
        List<User> userList = gson.fromJson(jsonList, new TypeToken<List<User>>() {
        }.getType());

        userList.forEach(System.out::println);
    }

    /**
     * Map 与 JSON 转换
     */
    @Test
    public void testJson2Map() {
        Gson gson = new Gson();

        // JSON → Map
        String json = """
                {"name":"张三","age":25,"lovers":["李四","王五"],"info":{"gender":"male","height":1.75}}
                """;
        Map<String, Object> map = gson.fromJson(json, new TypeToken<Map<String, Object>>() {
        }.getType());
        System.out.println(map.get("name") + " age: " + map.get("age")); // 输出：张三 25.0

        // Map → JSON
        String mapJson = gson.toJson(map);
        System.out.println(mapJson); // 输出：{"name":"张三","age":25.0}（注意：int 会转为 double，需自定义处理
    }

    /**
     * JsonObject 与 JSON 转换
     */
    @Test
    public void testJsonObject() {
// 1. 构建 JSON 对象（类似 FastJSON JSONObject.put()）
        JsonObject jsonObject = new JsonObject();
        jsonObject.addProperty("name", "张三"); // 添加字符串
        jsonObject.addProperty("age", 25);      // 添加数字
        jsonObject.addProperty("isVip", true);  // 添加布尔值

        // 嵌套 JsonObject（类似 JSONObject 嵌套）
        JsonObject address = new JsonObject();
        address.addProperty("city", "北京");
        address.addProperty("street", "XX路");
        jsonObject.add("address", address); // 非基本类型用 add()

        // 2. 转为 JSON 字符串（类似 JSONObject.toJSONString()）
        String jsonStr = jsonObject.toString();
        System.out.println(jsonStr);
        // 输出：{"name":"张三","age":25,"isVip":true,"address":{"city":"北京","street":"XX路"}}

        // 3. 解析 JSON 字符串为 JsonObject（类似 JSONObject.parseObject()）
        Gson gson = new Gson();
        JsonObject parsedObj = gson.fromJson(jsonStr, JsonObject.class);

        // 4. 获取值（类似 JSONObject.getXXX()）
        String name = parsedObj.get("name").getAsString(); // 字符串
        int age = parsedObj.get("age").getAsInt();         // 整数
        boolean isVip = parsedObj.get("isVip").getAsBoolean(); // 布尔值
        String city = parsedObj.getAsJsonObject("address").get("city").getAsString();

        System.out.println(name); // 张三
        System.out.println(age);  // 25
        System.out.println(isVip); // true
        System.out.println(city); // 北京

    }

    @Test
    public void testDateTime() {
        Gson gson = new GsonBuilder()
                .setPrettyPrinting() // 格式化 JSON 输出（换行、缩进）
                .registerTypeAdapter(Date.class, new DateTypeAdapter())
                .registerTypeAdapter(Instant.class, new InstantTypeAdapter())
                .registerTypeAdapter(ZonedDateTime.class, new ZonedDateTimeTypeAdapter())
                .registerTypeAdapter(OffsetDateTime.class, new OffsetDateTimeTypeAdapter())
                .registerTypeAdapter(LocalDateTime.class, new LocalDateTimeTypeAdapter())
                .registerTypeAdapter(LocalDate.class, new LocalDateTypeAdapter())
                .registerTypeAdapter(LocalTime.class, new LocalTimeTypeAdapter())
                .create();
        System.out.println("Date: " + new Date() + ", toJson = " + gson.toJson(new Date()));

        try {
            System.err.println("Instant: " + gson.toJson(Instant.now()));
        } catch (Exception e) {
            e.printStackTrace();
        }
        try {
            System.out.println("LocalDateTime: " + gson.toJson(LocalDateTime.now()));
        } catch (Exception e) {
            e.printStackTrace();
        }
        try {
            System.out.println("OffsetDateTime: " + gson.toJson(OffsetDateTime.now()));
        } catch (Exception e) {
            e.printStackTrace();
        }
        try {
            System.out.println("ZonedDateTime: " + gson.toJson(ZonedDateTime.now()));
        } catch (Exception e) {
            e.printStackTrace();
        }
        try {
            TimeDemoVO vo = new TimeDemoVO(System.currentTimeMillis());
            System.out.println(vo);
            System.out.println(gson.toJson(vo));
        } catch (Exception e) {
            e.printStackTrace();
        }

//        vo.setOk(true);
//        String json = JSON.toJSONString(vo, true);
//        System.out.println(json);
//        TimeDemoVO tmp = JSON.parseObject(json, TimeDemoVO.class);
//        System.out.println(tmp.getLocalDateTime().toString());
//        String a = "{\"time\": \"2022-05-06T09:11:22.535Z\"}";
//        JSONObject o = JSON.parseObject(a);
//        System.out.println(o);
//        System.out.println(o.getObject("time", LocalDateTime.class));
//        System.out.println(o.getObject("time", OffsetDateTime.class));
    }

    @Test
    public void testJsonBuilder() {
        Gson gson = new GsonBuilder()
                .setPrettyPrinting() // 格式化 JSON 输出（换行、缩进）
                .serializeNulls() // 序列化 null 值（默认不序列化）
                .setDateFormat("yyyy-MM-dd HH:mm:ss") // 日期格式化（如 Date 类型转为指定格式字符串）
                .disableHtmlEscaping() // 禁用 HTML 转义（如不将 < 转为 \u003c）
                .registerTypeAdapter(Date.class, new DateTypeAdapter()) // 注册自定义 TypeAdapter
                .create();
    }

    /**
     * 自定义 TypeAdapter（处理特殊类型）
     * <p>
     * 例如，处理 Date 类型的自定义序列化 / 反序列化：
     */
    public static class DateTypeAdapter extends TypeAdapter<Date> {
        // 序列化：Date → JSON 字符串
        @Override
        public void write(JsonWriter out, Date value) throws IOException {
            if (value == null) {
                out.nullValue();
                return;
            }
            out.value(value.getTime());
        }

        // 反序列化：JSON 字符串 → Date
        @Override
        public Date read(JsonReader in) throws IOException {
            if (in.peek() == null) {
                in.nextNull();
                return null;
            }
            long ts = in.nextLong();
            try {
                return new Date(ts);
            } catch (Exception e) {
                throw new IOException("日期格式错误: " + ts, e);
            }
        }
    }

    public static class LocalDateTimeTypeAdapter extends TypeAdapter<LocalDateTime> {
        // 序列化：Date → JSON 字符串
        @Override
        public void write(JsonWriter out, LocalDateTime value) throws IOException {
            if (value == null) {
                out.nullValue();
                return;
            }
            out.value(value.toString());
        }

        // 反序列化：JSON 字符串 → Date
        @Override
        public LocalDateTime read(JsonReader in) throws IOException {
            if (in.peek() == null) {
                in.nextNull();
                return null;
            }
            String str = in.nextString();
            try {
                return LocalDateTime.parse(str);
            } catch (Exception e) {
                throw new IOException("日期格式错误: " + str, e);
            }
        }
    }

    public static class LocalDateTypeAdapter extends TypeAdapter<LocalDate> {
        // 序列化：Date → JSON 字符串
        @Override
        public void write(JsonWriter out, LocalDate value) throws IOException {
            if (value == null) {
                out.nullValue();
                return;
            }
            out.value(value.toString());
        }

        // 反序列化：JSON 字符串 → Date
        @Override
        public LocalDate read(JsonReader in) throws IOException {
            if (in.peek() == null) {
                in.nextNull();
                return null;
            }
            String str = in.nextString();
            try {
                return LocalDate.parse(str);
            } catch (Exception e) {
                throw new IOException("日期格式错误: " + str, e);
            }
        }
    }

    public static class LocalTimeTypeAdapter extends TypeAdapter<LocalTime> {
        // 序列化：Date → JSON 字符串
        @Override
        public void write(JsonWriter out, LocalTime value) throws IOException {
            if (value == null) {
                out.nullValue();
                return;
            }
            out.value(value.toString());
        }

        // 反序列化：JSON 字符串 → Date
        @Override
        public LocalTime read(JsonReader in) throws IOException {
            if (in.peek() == null) {
                in.nextNull();
                return null;
            }
            String str = in.nextString();
            try {
                return LocalTime.parse(str);
            } catch (Exception e) {
                throw new IOException("日期格式错误: " + str, e);
            }
        }
    }

    public static class OffsetDateTimeTypeAdapter extends TypeAdapter<OffsetDateTime> {
        // 序列化：OffsetDateTime → JSON 字符串
        @Override
        public void write(JsonWriter out, OffsetDateTime value) throws IOException {
            if (value == null) {
                out.nullValue();
                return;
            }
            out.value(value.toString());
        }

        // 反序列化：JSON 字符串 → OffsetDateTime
        @Override
        public OffsetDateTime read(JsonReader in) throws IOException {
            if (in.peek() == null) {
                in.nextNull();
                return null;
            }
            String str = in.nextString();
            try {
                return OffsetDateTime.parse(str);
            } catch (Exception e) {
                throw new IOException("OffsetDateTime 格式错误: " + str, e);
            }
        }
    }

    public static class ZonedDateTimeTypeAdapter extends TypeAdapter<ZonedDateTime> {
        // 序列化：ZonedDateTime → JSON 字符串
        @Override
        public void write(JsonWriter out, ZonedDateTime value) throws IOException {
            if (value == null) {
                out.nullValue();
                return;
            }
            out.value(value.toString());
        }

        // 反序列化：JSON 字符串 → ZonedDateTime
        @Override
        public ZonedDateTime read(JsonReader in) throws IOException {
            if (in.peek() == null) {
                in.nextNull();
                return null;
            }
            String str = in.nextString();
            try {
                return ZonedDateTime.parse(str);
            } catch (Exception e) {
                throw new IOException("ZonedDateTime 格式错误: " + str, e);
            }
        }
    }

    public static class InstantTypeAdapter extends TypeAdapter<Instant> {
        @Override
        public void write(JsonWriter out, Instant value) throws IOException {
            if (value == null) {
                out.nullValue();
                return;
            }
            // 将Instant转换为毫秒时间戳
            out.value(value.toEpochMilli());
        }

        @Override
        public Instant read(JsonReader in) throws IOException {
            if (in.peek() == null) {
                in.nextNull();
                return null;
            }
            long ts = in.nextLong();
            try {
                return Instant.ofEpochMilli(ts);
            } catch (Exception e) {
                throw new IOException("Instant 格式错误: " + ts, e);
            }
        }
    }


}
